#include "../PipeSocket.h"

#include <unistd.h>
#include <fcntl.h>

// TODO: move this to a centrel control place
#include <string.h>

using namespace Greatewall::Interface;

namespace Greatewall
{
    namespace Network
    {
        void
        PipeSocket::createPipe(SysSockHandler& in, SysSockHandler& out)
        {
            SysSockHandler hdls[2];

            int res = pipe(hdls);

            if (!res)
            {
                out = hdls[0];
                in  = hdls[1];
            }
            else
            {
                /// Print out some error message
                /// And reset the value
                in = out = -1;
            }
        }

        bool
        PipeSocket::write(const char* buf, ssize_t& size)
        {
            if (!_inited)
            {
                // TODO: Error handling
                return false;
            }

            if (_out == -1)
            {
                // TODO: Error handling
                return false;
            }

            if (buf && size <= 0)
            {
                // TODO: Error handling
                return false;
            }

            if (buf && size > 0)
            {
                ssize_t wrote = ::write(_hdl, buf, size);

                if (wrote == -1)
                {
                    // TODO: Error handling
                    size = wrote;
                    return false;
                }

                if (wrote < size)
                {
                    char* qbuf = new char(size - wrote);
                    memcpy(qbuf, buf + wrote, size - wrote);
                    _outbound_queue.push(OutboundNode(qbuf, size - wrote));
                    size = wrote;
                }
            }
            else
                if (_outbound_queue.size())
                {
                    OutboundNode& qbuf = _outbound_queue.front();

                    if (qbuf._buf && qbuf._size && qbuf._size > qbuf._offset)
                    {
                        ssize_t wrote = ::write(_hdl, qbuf._buf + qbuf._offset, qbuf._size - qbuf._offset);

                        if (wrote == -1)
                        {
                            // TODO: Error handling
                            size = wrote;
                            return false;
                        }

                        qbuf._offset += wrote;
                        if (qbuf._offset >= qbuf._size)
                            _outbound_queue.pop();

                        size = wrote;
                    }
                    else
                        _outbound_queue.pop();
                }
            return true;
        }

        bool
        PipeSocket::read(char*& buf, ssize_t& size)
        {
            if (!_inited)
            {
                // TODO: Error handling
                return false;
            }

            if (_in == -1)
            {
                // TODO: Error handling
                return false;
            }

            if (buf && size <= 0)
            {
                // TODO: Error handling
                return false;
            }

            char*   rbuf  = buf ? buf  : (char*)aligned_buf;
            ssize_t rsize = buf ? size : sizeof(aligned_buf);

            size = ::read(_hdl, rbuf, rsize);

            if (size == -1)
            {
                // Error handling
                return false;
            }

            // Return the static buffer, if read buffer is not provided
            if (!buf) buf = rbuf;

            return true;
        }

        bool
        PipeSocket::initPipeSock()
        {
            if (_inited)
            {
                // TODO: Error handling
                return false;
            }

            createPipe(_in, _out);

            if (_in == -1 || _out == -1)
            {
                // TODO: Error handling
                return false;
            }

            // Make both of the descriptor in non-block mode
            if (fcntl(_out, F_SETFL, O_NONBLOCK) == -1)
            {
                // TODO: Error handling
                return false;
            }

            if (fcntl(_in, F_SETFL, O_NONBLOCK) == -1)
            {
                // TODO: Error handling
                return false;
            }

            _inited = true;

            return true;
        }

        void
        PipeSocket::close()
        {
            if (_in != -1)
                ::close(_in);

            if (_out != -1)
                ::close(_out);
        }
    }
}
